#include "filesocket.h"

#include "databasemagr.h"
#include "command.h"
#include "config.h"

#include <QDebug>
#include <QDataStream>
#include <QApplication>
#include <QFileInfo>

FileSocket::FileSocket(QObject *parent, QTcpSocket *tcpSocket) :
    QObject(parent)
{
    // 将整个大的文件分成很多小的部分进行发送，每部分为4字节
    loadSize            = 50 * 1024;
    ullSendTotalBytes   = 0;
    ullRecvTotalBytes   = 0;
    bytesWritten        = 0;
    bytesToWrite        = 0;
    bytesReceived       = 0;

    fileNameSize        = 0;
    m_bBusy             = false;

    m_nUserId           = -1;
    m_nWindowId         = -1;

    // 本地文件存储
    fileToSend = new QFile(this);
    fileToRecv = new QFile(this);

    // 客户端
    if (tcpSocket == NULL) m_tcpSocket = new QTcpSocket(this);
    m_tcpSocket = tcpSocket;

    // 我们更新进度条
    connect(m_tcpSocket, SIGNAL(readyRead()), this, SLOT(SltReadyRead()));
    connect(m_tcpSocket, SIGNAL(disconnected()), this, SIGNAL(signalDisConnected()));
    // 当有数据发送成功时，我们更新进度条
    connect(m_tcpSocket, SIGNAL(bytesWritten(qint64)), this, SLOT(SltUpdateClientProgress(qint64)));
}

FileSocket::~FileSocket()
{

}

/**
 * @brief FileSocket::Close
 * 关闭
 */
void FileSocket::Close()
{
    m_tcpSocket->abort();
}

/**
 * @brief FileSocket::CheckUserId
 * 用户socket检测，通过此函数进行判断连接的socket
 * @param nId
 * @param winId
 * @return
 */
bool FileSocket::CheckUserId(const qint32 nId, const qint32 &winId)
{
    return ((m_nUserId == nId) && (m_nWindowId == winId));
}

/**
 * @brief FileSocket::startTransferFile
 * 下发文件
 * @param type 0 表示单纯的语音文件，1表示文字+语音，客户端只收不显示
 * @param fileName  文件名
 */
void FileSocket::StartTransferFile(QString fileName)
{
    if (m_bBusy) return;

    if (!m_tcpSocket->isOpen()) {
        return;
    }

    // 要发送的文件
    fileToSend = new QFile((-2 == m_nWindowId ? Config::m_strHeadPath : Config::m_strRecvPath) + fileName);

    if (!fileToSend->open(QFile::ReadOnly))
    {
        qDebug() << "open file error!";
        return;
    }

    ullSendTotalBytes = fileToSend->size(); // 文件总大小

    QDataStream sendOut(&outBlock, QIODevice::WriteOnly);
    sendOut.setVersion(QDataStream::Qt_4_8);

    QString currentFileName = fileName.right(fileName.size() - fileName.lastIndexOf('/')-1);

    // 依次写入总大小信息空间，文件名大小信息空间，文件名
    sendOut << qint64(0) << qint64(0) << currentFileName;

    // 这里的总大小是文件名大小等信息和实际文件大小的总和
    ullSendTotalBytes += outBlock.size();

    // 返回outBolock的开始，用实际的大小信息代替两个qint64(0)空间
    sendOut.device()->seek(0);
    sendOut << ullSendTotalBytes << qint64((outBlock.size() - sizeof(qint64)*2));

    // 发送完头数据后剩余数据的大小
    bytesToWrite = ullSendTotalBytes - m_tcpSocket->write(outBlock);

    outBlock.resize(0);
    m_bBusy = true;
    qDebug() << "Begin to send file" << fileName << m_nUserId << m_nWindowId;
}

/**
 * @brief FileSocket::SltUpdateClientProgress
 * @param numBytes
 */
void FileSocket::SltUpdateClientProgress(qint64 numBytes)
{
    // 已经发送数据的大小
    bytesWritten += (int)numBytes;
    // 如果已经发送了数据
    if (bytesToWrite > 0)
    {
        // 每次发送loadSize大小的数据，这里设置为4KB，如果剩余的数据不足4KB，就发送剩余数据的大小
        outBlock = fileToSend->read(qMin(bytesToWrite, loadSize));

        // 发送完一次数据后还剩余数据的大小
        bytesToWrite -= (int)m_tcpSocket->write(outBlock);

        // 清空发送缓冲区
        outBlock.resize(0);
    }
    else
    {
        // 如果没有发送任何数据，则关闭文件
        if (fileToSend->isOpen())
            fileToSend->close();
    }

    // 发送完毕
    if (bytesWritten >= ullSendTotalBytes)
    {
        if (fileToSend->isOpen())
            fileToSend->close();

        bytesWritten = 0;  // clear fot next send
        ullSendTotalBytes = 0;
        bytesToWrite = 0;
        qDebug() << "send ok" << fileToSend->fileName();
        FileTransFinished();
    }
}

void FileSocket::displayError(QAbstractSocket::SocketError)
{
    m_tcpSocket->abort();
}

void FileSocket::FileTransFinished()
{
    ullSendTotalBytes   = 0;
    ullRecvTotalBytes   = 0;
    bytesWritten        = 0;
    bytesToWrite        = 0;
    bytesReceived       = 0;

    fileNameSize        = 0;
    m_bBusy = false;
}

// 更新进度条，实现文件的接收
void FileSocket::SltReadyRead()
{
    QDataStream in(m_tcpSocket);
    in.setVersion(QDataStream::Qt_4_8);

    // 连接时的消息
    if (0 == bytesReceived && (-1 == m_nUserId) && (-1 == m_nWindowId) &&
            (m_tcpSocket->bytesAvailable() == (sizeof(qint32) * 2)))
    {
        // 保存ID，方便发送文件
        in >> m_nUserId >> m_nWindowId;
        qDebug() << "File server Get userId" << m_nUserId << m_nWindowId;
        Q_EMIT signalConnected();
        return;
    }

    // 如果接收到的数据小于等于20个字节，那么是刚开始接收数据，我们保存为头文件信息
    if (bytesReceived <= (sizeof(qint64)*2))
    {
        int nlen = sizeof(qint64) * 2;
        // 接收数据总大小信息和文件名大小信息
        if ((m_tcpSocket->bytesAvailable() >= nlen) && (fileNameSize == 0))
        {
            in >> ullRecvTotalBytes >> fileNameSize;
            if (0 != ullRecvTotalBytes) bytesReceived += nlen;
        }

        // 接收文件名，并建立文件
        if((m_tcpSocket->bytesAvailable() >= (qint64)fileNameSize) &&
                ((qint64)fileNameSize != 0) &&
                (0 != ullRecvTotalBytes))
        {
            in >> fileReadName;
            bytesReceived += fileNameSize;

            fileToRecv->setFileName((-2 == m_nWindowId ? Config::m_strHeadPath : Config::m_strRecvPath) + fileReadName);

            if (!fileToRecv->open(QFile::WriteOnly | QIODevice::Truncate))
            {
                qDebug() << "open file error" << fileReadName;
                return;
            }
            qDebug() << "begin to recv files" << fileReadName;
        }
    }

    //如果接收的数据小于总数据，那么写入文件
    if (bytesReceived < ullRecvTotalBytes)
    {
        bytesReceived += m_tcpSocket->bytesAvailable();
        inBlock = m_tcpSocket->readAll();

        if (fileToRecv->isOpen())
            fileToRecv->write(inBlock);

        inBlock.resize(0);
    }

    // 接收数据完成时
    if ((bytesReceived >= ullRecvTotalBytes) && (0 != ullRecvTotalBytes))
    {
        fileToRecv->close();
        bytesReceived = 0; // clear for next receive
        ullRecvTotalBytes = 0;
        fileNameSize = 0;
        qDebug() << "recv ok" << fileToRecv->fileName();
        // 数据接受完成
        FileTransFinished();
    }
}

